﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using CrushGame.DelegateSpace;

namespace CrushGame
{
    /// <summary>
    /// Manages and organizes the things and persons that occupy the world. 
    /// </summary>
    public class ObjectManager
    {
        //Lists of objects in the world
        LinkedList<Person> Persons;
        LinkedList<Thing> Things;

        //Has the player been spawned?
        bool _playerSpawned;
        
        /// <summary>
        /// Property to get ahold of the Number of Persons we have
        /// </summary>
        public int PersonCount
        {
            get { return Persons.Count; }
        }
        /// <summary>
        /// Property to get ahold of the Number of Things we have
        /// </summary>
        public int ThingCount
        {
            get { return Things.Count; }
        }

        

        /// <summary>
        /// Constructor
        /// </summary>
        public ObjectManager()
        {
            //Initialize our lists as empty
            Persons = new LinkedList<Person>();
            Things = new LinkedList<Thing>();

            //We haven't had the player spawned yet
            _playerSpawned = false;
        }

        /// <summary>
        /// Helper function when sorting the templated Spawn function
        /// </summary>
        /// <param name="NewPerson">Person to add to our person list</param>
        void AddPerson(object NewPerson)
        {
            Persons.AddLast((Person)NewPerson);
        }

        /// <summary>
        /// Helper function when  sorting the templated Spawn Function
        /// </summary>
        /// <param name="NewThing">Thing to add to our Things list</param>
        void AddThing(object NewThing)
        {
            Things.AddLast((Thing)NewThing);
        }
        
        /// <summary>
        /// Spawns objects for the world
        /// </summary>
        /// <typeparam name="T">Type of the object to spawn</typeparam>
        /// <param name="Content">ContentManager needed to load the sprites for the object</param>
        /// <param name="Location">Location to spawn at. 0, 0, 0 if left empty</param>
        /// <param name="InitialVelocity">Initial velocity of the object. 0, 0, 0 if left empty</param>
        /// <param name="SpriteName">Optional asset name to override the default image for the object</param>
        /// <returns>Returns a ref to the created object</returns>
        public T Spawn<T>(ContentManager Content, Vector2 Location = default(Vector2), Vector2 InitialVelocity = default(Vector2), string SpriteName = "") where T : IBasicInterface, new()
        {
            //Spawn our cooky could-be-anything object
            T NewObj = new T();

            //It MUST have our basic interface, so set its basic data
            NewObj.Location = Location;
            NewObj.Velocity = InitialVelocity;

            //Initialize the content!
            NewObj.ContentInitialize(Content, SpriteName);

            //We are only allowed to have one player, so let's restrain the ability to make more
            if (!_playerSpawned && NewObj is Player)
            {
                _playerSpawned = true;
                PlayerHasSpawned(this, new PlayerSpawnedEventArgs(NewObj));
            }
            else if (NewObj is Player)
            {
                //We are trying to make another? Fuck That
                return default(T);
            }

            //Now if it is a person, add it to the person list
            if (NewObj is Person)
            {
                AddPerson(NewObj);
            }
            //otherwise, add it to the things' list
            else if (NewObj is Thing)
            {
                AddThing(NewObj);
            }

            return NewObj;
        }

        /// <summary>
        /// Updates all of the objects
        /// </summary>
        /// <param name="DeltaTime">Elapsed time since last frame</param>
        public void Update(float DeltaTime)
        {
            //Persons
            foreach (Person curr in Persons)
            {
                curr.Update(DeltaTime);
            }

            //Things
            foreach (Thing curr in Things)
            {
                curr.Update(DeltaTime);
            }

        }

        /// <summary>
        /// Draws all of the objects
        /// </summary>
        /// <param name="Batch">The SpriteBatch to draw to</param>
        public void Draw(SpriteBatch Batch)
        {
            //Persons
            foreach (Person curr in Persons)
            {
                curr.Draw(Batch);
            }

            //Things
            foreach (Thing curr in Things)
            {
                curr.Draw(Batch);
            }
        }

        /// <summary>
        /// Shuts down the Object manager, and makes sure the objects get shut down
        /// </summary>
        public void Shutdown()
        {
            //Persons
            foreach (Person curr in Persons)
            {
                curr.Shutdown();
            }

            //Clear the list
            Persons.Clear();

            //Things
            foreach (Thing curr in Things)
            {
                curr.Shutdown();
            }

            //Clear the other list
            Things.Clear();
        }

        public event PlayerSpawnedHandler PlayerHasSpawned;
    }
}
